package rdbms.ddl;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import valuetypes.CharacterValue;
import valuetypes.DBValue;
import valuetypes.NumericValue;
import exception.DBValueException;
import exception.IllegalNameException;
import exception.InvalidFieldException;


public class Database {

	private static int tableIdGen = 0;
	private LinkedHashMap<Integer, Table> tables;
	private LinkedHashMap<String, Integer> indexTableName;
	private CharacterValue name;
	private NumericValue id;
	
	public Database(NumericValue id, String name) throws IllegalNameException{		
		try {
			this.name = new CharacterValue(name);
		} catch (DBValueException e) {
			throw IllegalNameException.fromDBValueException(e);
		}
		tables = new LinkedHashMap<>();
		indexTableName  = new LinkedHashMap<>();
		this.id = id;
	}
	
	public Database(byte[] data) throws DBValueException{
		tables = new LinkedHashMap<>();
		indexTableName  = new LinkedHashMap<>();
		id = new NumericValue(data, 0);
		int offset = id.getSize();
		name = new CharacterValue(data, offset);
	}
	
	public Table createTable(String tableName, List<Field> fields, Field[] primaryKey) throws IllegalNameException, DBValueException, InvalidFieldException {
		if(tables.containsKey(tableName)){
			throw IllegalNameException.tableExists(name.toString(), tableName);
		}
		else {
			NumericValue tableId = new NumericValue(++tableIdGen);
			Table table = new Table(this, tableId, tableName, fields, primaryKey);
			addTable(table);
			return table;
		}		
	}
	
	public Table getTable(int tableId){
		return tables.get(tableId);
	}
	
	public Table getTable(String tableName){
		return getTable(indexTableName.get(tableName));
	}

	public String getName() {
		return name.toString();
	}
	
	public boolean dropTable(int id){
		Table tableToDrop = tables.get(id);
		if(tableToDrop == null)return false;
		if(tableToDrop.isReferredExternally())return false;
		tableToDrop.cleanUp();
		indexTableName.remove(tableToDrop.getName());
		tables.remove(id);
		return true;
	}
	
	@Override
	public String toString() {
		return id + "\t| " + name;
	}

	public byte[] serialize() {
		return DBValue.valuesAsData(id, name);
	}

	public ArrayList<Table> getTables() {
		return new ArrayList<Table>(tables.values());
	}

	public CharacterValue getNameValue() {
		return name;
	}

	public NumericValue getId() {
		return id;
	}

	public void addTable(Table table) {
		int tableId = table.getId().getValue();				
		if(tableId > tableIdGen){
			tableIdGen = table.getId().getValue();
		}
		tables.put(tableId, table);
		indexTableName.put(table.getName(), tableId);
	}

	public Table getTable(NumericValue tableId) {
		return getTable(tableId.getValue());
	}	
	
}
